package org.eece262.taskshareserver;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

import javax.jdo.PersistenceManager;
import javax.servlet.http.*;

import org.edu.eece262.taskshare.Messages;
import org.edu.eece262.taskshare.Messages.ActionType;
import org.edu.eece262.taskshare.Messages.MessageToClient;
import org.edu.eece262.taskshare.Messages.MessageToServer;
import org.edu.eece262.taskshare.Messages.ResponseType;
import org.eece262.taskshareserver.ContactPair.StatusType;

import com.google.gson.Gson;
import com.google.gson.JsonParseException;

public class AndroidTaskShareServlet extends HttpServlet {
	private static final long serialVersionUID = -6770100562009325024L;

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
		response.setContentType("text/plain");
		try {
			response.getWriter().println(
					"TaskShare does not respond to GET method.");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	public void doPost(HttpServletRequest request, HttpServletResponse response) {
		
		Messages.MessageToClient messageToClient = null; 
		Messages.MessageToServer messageToServer = null;
		
		String JSONMessageToServer = request.getParameter("JSONMessageToServer");
		
		if(JSONMessageToServer == null) {
			try {
				response.getWriter().println("Error - no MessageToServer.");
			} catch (IOException e) {
				e.printStackTrace();
			}
			return;
		}
		
		try {
			Gson gson = new Gson();
			messageToServer = gson.fromJson(JSONMessageToServer, Messages.MessageToServer.class);
		} catch(JsonParseException e) {
			try {
				response.getWriter().println("Error - parsing JSON.");
			} catch (IOException e2) {
				e2.printStackTrace();
			}
			return;
		}
		
		PersistenceManager pm = PMF.get().getPersistenceManager();
		CleanseUUIDs();
			
		switch(messageToServer.getAction()) {
			case CREATEUSER: 
			{				
				String query = "SELECT FROM " + User.class.getName() + 
					" WHERE Username == '" + messageToServer.getUser() + "'";
				List<User> users = (List<User>) pm.newQuery(query).execute();
				
				if(users.isEmpty()) {
					User uNew = new User(messageToServer.getUser(), messageToServer.getPassword(), 
							messageToServer.getPhone_number(), messageToServer.getEmail(), messageToServer.getName(),
							messageToServer.getAccess_level());
					pm.makePersistent(uNew);
					
					messageToClient = new MessageToClient()
						.setResponse(ResponseType.SUCCESS)
						.setUser(messageToServer.getUser())
						.setAction(ActionType.CREATEUSER);
				} else {
					messageToClient = new MessageToClient()
						.setResponse(ResponseType.FAILURE)
						.setUser(messageToServer.getUser())
						.setAction(ActionType.CREATEUSER);
				}
			}
			break;
					
			case LOGIN:
			{
				String query = "SELECT FROM " + User.class.getName() + 
					" WHERE Username == '" + messageToServer.getUser() + "'";
				List<User> users = (List<User>) pm.newQuery(query).execute();
				
				if(users.isEmpty()) {
					messageToClient = new MessageToClient()
						.setResponse(ResponseType.FAILURE)
						.setUser(messageToServer.getUser())
						.setAction(ActionType.LOGIN);
				} else if (users.get(0).getPassword().equals(messageToServer.getPassword())) {
					UUID userLogin = UUID.randomUUID();
					UserUUIDPair userUUID = new UserUUIDPair(messageToServer.getUser(), userLogin.toString());
					pm.makePersistent(userUUID);
					messageToClient = new MessageToClient()
					.setResponse(ResponseType.SUCCESS)
					.setUser(messageToServer.getUser())
					.setAction(ActionType.LOGIN)
					.setUuid(userLogin.toString());
				} else {
					messageToClient = new MessageToClient()
						.setResponse(ResponseType.FAILURE)
						.setUser(messageToServer.getUser())
						.setAction(ActionType.LOGIN);
				}
			}
			break;
			
			case LOGOUT: {
				String query = "SELECT FROM " + UserUUIDPair.class.getName() + 
					" WHERE user == '" + messageToServer.getUser() + "' && uuid == '" 
					+ messageToServer.getUuid() + "'";
				
				List<UserUUIDPair> users = (List<UserUUIDPair>) pm.newQuery(query).execute();
				
				if(users.size() > 0) {
					for(int i = 0; i < users.size(); i++) {
						pm.deletePersistent(users.get(i));
					}
					messageToClient = new MessageToClient().setAction(ActionType.LOGOUT).setResponse(ResponseType.SUCCESS); 
				} else {
					messageToClient = new MessageToClient().setAction(ActionType.LOGOUT).setResponse(ResponseType.FAILURENOTLOGGEDIN);
				}
			}
			break;
					
			// All things that require the user to be logged in
			default:
			{
				// check if user is logged-in
				if(isLoggedIn(messageToServer.getUser(), messageToServer.getUuid())) {
					switch(messageToServer.getAction()) {
						
						case POLL: {
							messageToClient = poll(messageToServer);
						}
						break;
						
						////////////////////
						// TASK MANAGEMENT//
						////////////////////
							case ADDTASK: {
								messageToClient = new MessageToClient()
									.setAction(ActionType.ADDTASK)
									.setUser(messageToServer.getUser())
									.setResponse(ResponseType.SUCCESS);
								for(int i = 0; i < messageToServer.getTasks().size(); i++) {
									Task newTask = new Task(messageToServer.getTasks().get(i), messageToServer.getUser());
									pm.makePersistent(newTask);
									messageToClient.addTask(newTask.getMessagesTask().setTimestamp(System.currentTimeMillis()));
								}
							}
							break;	
							
							case UPDATETASK: {
								messageToClient = new MessageToClient().setAction(ActionType.UPDATETASK).setResponse(ResponseType.SUCCESS);
								boolean tasksFound = true;
								for(int i = 0; i < messageToServer.getTasks().size(); i++) {
									Task currentTask = pm.getObjectById(Task.class, messageToServer.getTasks().get(i).getGlobal_id());
									if(currentTask == null) {
										tasksFound = false;
										messageToClient.setResponse(ResponseType.FAILURE);
										break;
									}
									if(!currentTask.getUser().equalsIgnoreCase(messageToServer.getUser())) {
										tasksFound = false;
										messageToClient.setResponse(ResponseType.FAILURENOTPERMITTED);
										break;
									}
								}
								if(tasksFound) {
									for(int i = 0; i < messageToServer.getTasks().size(); i++) {
										Task newTask = pm.getObjectById(Task.class, messageToServer.getTasks().get(i).getGlobal_id());
										if(newTask == null) {
											messageToClient.setResponse(ResponseType.FAILURE);
											break;
										} else {
											newTask.update(messageToServer.getTasks().get(i));
										}
									}
								}
							}
							break;
							
							case ASSIGNTASK: {
								//TODO: Handle ASSIGNTASK
							}
							break;
							
							case ASSIGNTOGROUPONESHOT: {
								//TODO: Handle ASSIGNTOGROUPONESHOT
							}
							break;
							
							case ASSIGNTOGROUPMULTIPLECOMPLETION: {
								//TODO: Handle ASSIGNTOGROUPMULTIPLECOMPLETION
							}
							break;
							
							case DELETETASK: {
								messageToClient = new MessageToClient().setAction(ActionType.DELETETASK).setResponse(ResponseType.SUCCESS);
								boolean tasksFound = true;
								for(int i = 0; i < messageToServer.getTasks().size(); i++) {
									Task currentTask = pm.getObjectById(Task.class, messageToServer.getTasks().get(i).getGlobal_id());
									if(currentTask == null) {
										tasksFound = false;
										messageToClient.setResponse(ResponseType.FAILURE);
										break;
									}
									if(!currentTask.getUser().equalsIgnoreCase(messageToServer.getUser())) {
										tasksFound = false;
										messageToClient.setResponse(ResponseType.FAILURENOTPERMITTED);
										break;
									}
								}
								if(tasksFound) {
									for(int i = 0; i < messageToServer.getTasks().size(); i++) {
										Task currentTask = pm.getObjectById(Task.class, messageToServer.getTasks().get(i).getGlobal_id());
										pm.deletePersistent(currentTask);
									}
								}
							}
							
							case GETTASKS: {
								String tasksQuery = "SELECT FROM " + Task.class.getName() + 
									" WHERE user == '" + messageToServer.getUser() + "'";
								List<Task> tasksResult = (List<Task>) pm.newQuery(tasksQuery).execute();

								messageToClient = new MessageToClient().setAction(ActionType.GETTASKS).setResponse(ResponseType.SUCCESS);
								
								for(int i = 0; i < tasksResult.size(); i++) {
									messageToClient.addTask(tasksResult.get(i).getMessagesTask());
								}
							}
							break;
							
						////////////////////////
						// CONTACT MANAGEMENT //
						////////////////////////
							case SEARCHCONTACT: {
								// Check if request has been made the other direction.
								String contactsQuery = "SELECT FROM " + User.class.getName() + 
									" WHERE Username == '" + messageToServer.getContact_user() + "'";
								List<User> contactsResult = (List<User>) pm.newQuery(contactsQuery).execute();
								if (contactsResult.isEmpty()) {
									messageToClient = new MessageToClient()
									.setAction(ActionType.SEARCHCONTACT)
									.setContact_user(messageToServer.getContact_user())
									.setResponse(ResponseType.FAILURE);
								} else {
									LinkedList<Messages.User> results = new LinkedList<Messages.User>();
									Iterator<User> currentUserIterator =  contactsResult.iterator();
									while(currentUserIterator.hasNext()) {
										User currentUser = currentUserIterator.next();
										if (currentUser.getAccessLevel() != 0)
											results.add(currentUser.getMessagesUser());
									}
									messageToClient = new MessageToClient()
									.setAction(ActionType.SEARCHCONTACT)
									.setContact_user(messageToServer.getContact_user())
									.setResponse(ResponseType.SUCCESS);
									messageToClient.setUsers(results);
								}
							}
							break;
							
							case ADDCONTACT: {
								// Check if request has been made the other direction.
								String contactsQuery = "SELECT FROM " + ContactPair.class.getName() + 
									" WHERE user1 == '" + messageToServer.getUser() + "' && user2 == '" + messageToServer.getContact_user() + "'";
								List<ContactPair> contactsResult = (List<ContactPair>) pm.newQuery(contactsQuery).execute();
								messageToClient = new MessageToClient()
								.setAction(ActionType.ADDCONTACT)
								.setContact_user(messageToServer.getContact_user())
								.setResponse(ResponseType.ONHOLD);
								// If so, send back an acceptances in both directions
								if(contactsResult != null && contactsResult.size() > 0 && contactsResult.get(0).getStatus() == ContactPair.StatusType.REQUESTED) {
									contactsResult.get(0).setStatus(ContactPair.StatusType.ACCEPTED);
									contactsQuery = "SELECT FROM " + ContactPair.class.getName() + 
										" WHERE user1 == '" + messageToServer.getContact_user()+ "' && user2 == '" + messageToServer.getUser() + "'";
									List<ContactPair> contactsResult2 = (List<ContactPair>) pm.newQuery(contactsQuery).execute();
									contactsResult2.get(0).setStatus(ContactPair.StatusType.ACCEPTED);
									messageToClient.setResponse(ResponseType.SUCCESS);
								} else if(contactsResult == null || contactsResult.size() == 0) {
									// If not, forward the request on to the end user.
									ContactPair newContactPair = new ContactPair(messageToServer.getUser(), messageToServer.getContact_user(), ContactPair.StatusType.WAITING);
									pm.makePersistent(newContactPair);
									ContactPair newContactPair2 = new ContactPair(messageToServer.getContact_user(), messageToServer.getUser(), ContactPair.StatusType.REQUESTED);
									pm.makePersistent(newContactPair2);
								} else if(contactsResult.size() > 0 && contactsResult.get(0).getStatus() == ContactPair.StatusType.DELTETED) {
									// If exists but is deleted, change to a request.
									contactsResult.get(0).setStatus(ContactPair.StatusType.WAITING);
									contactsQuery = "SELECT FROM " + ContactPair.class.getName() + 
										" WHERE user1 == '" + messageToServer.getContact_user()+ "' && user2 == '" + messageToServer.getUser() + "'";
									List<ContactPair> contactsResult2 = (List<ContactPair>) pm.newQuery(contactsQuery).execute();
									if(contactsResult2 != null && contactsResult2.size() > 0) {
										contactsResult2.get(0).setStatus(ContactPair.StatusType.REQUESTED);
									} else {
										ContactPair newContactPair = new ContactPair(messageToServer.getContact_user(), messageToServer.getUser(), ContactPair.StatusType.REQUESTED);
										pm.makePersistent(newContactPair);
									}
								}
							}
							break;
							
							case DELETECONTACT: {
								String contactsQuery = "SELECT FROM " + ContactPair.class.getName() + 
									" WHERE user1 == '" + messageToServer.getUser() + "' && user2 == '" + messageToServer.getContact_user() + "'";
								List<ContactPair> contactsResult = (List<ContactPair>) pm.newQuery(contactsQuery).execute();
								if(contactsResult != null && contactsResult.size() > 0) {
									contactsResult.get(0).setStatus(StatusType.DELTETED);
								}
								contactsQuery = "SELECT FROM " + ContactPair.class.getName() + 
									" WHERE user1 == '" + messageToServer.getContact_user()+ "' && user2 == '" + messageToServer.getUser() + "'";
								List<ContactPair> contactsResult2 = (List<ContactPair>) pm.newQuery(contactsQuery).execute();
								if(contactsResult2 != null && contactsResult2.size() > 0) {
									contactsResult2.get(0).setStatus(ContactPair.StatusType.DELTETED);
								}
								messageToClient = new MessageToClient()
								.setAction(ActionType.DELETECONTACT)
								.setContact_user(messageToServer.getContact_user())
								.setResponse(ResponseType.SUCCESS);
							}
							break;
							
							case GETCONTACTS: {
								String contactsQuery = "SELECT FROM " + ContactPair.class.getName() + 
									" WHERE user1 == '" + messageToServer.getUser() + "' && status == " + ContactPair.StatusType.ACTIVE;
								List<ContactPair> contactsResult = (List<ContactPair>) pm.newQuery(contactsQuery).execute();
								
								messageToClient = new  MessageToClient().setAction(ActionType.GETCONTACTS).setResponse(ResponseType.SUCCESS);
								
								for(int i = 0; i < contactsResult.size(); i++) {
									String userQuery = "SELECT FROM " + User.class.getName() + 
										" WHERE user1 == '" + contactsResult.get(i).getUser2() + "'";
									
									List<User> userResult = (List<User>) pm.newQuery(userQuery).execute();
									
									messageToClient.addUser(userResult.get(0).getMessagesUser());
								}
							}
							
						////////////////////////
						// GROUP MANAGEMENT //
						////////////////////////
							//TODO: ALL GROUP STUFF
					}
				} else {
					messageToClient = new MessageToClient().setAction(messageToServer.getAction()).setResponse(ResponseType.FAILURENOTLOGGEDIN);
				}
				
			}	
			break;
		}
		
		pm.close();
			
		if(messageToClient == null) {
			messageToClient = new MessageToClient().setAction(messageToServer.getAction()).setResponse(Messages.ResponseType.FAILURE);
		}
		
		messageToClient.setUser(messageToServer.getUser());
		
		try {
			Gson gson = new Gson();
			response.getWriter().print(gson.toJson(messageToClient));
		} catch(JsonParseException e) {
			try {
				response.getWriter().println("Error - parsing JSON.");
			} catch (IOException e2) {
				e2.printStackTrace();
			}
			return;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@SuppressWarnings("unchecked")
	private void CleanseUUIDs() {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		//Logins only remain valid for 15 minutes
		long compareTime = System.currentTimeMillis() - 60000*15;  
		
		String query = "SELECT FROM " + UserUUIDPair.class.getName() + 
		" WHERE time < " + compareTime;
		
		List<UserUUIDPair> users = (List<UserUUIDPair>) pm.newQuery(query).execute();
		
		for(int i = 0; i < users.size(); i++) {
			pm.deletePersistent(users.get(i));
		}
	}
	
	@SuppressWarnings("unchecked")
	private boolean isLoggedIn(String user, String uuid) {
		PersistenceManager pm = PMF.get().getPersistenceManager(); 
		String query = "SELECT FROM " + UserUUIDPair.class.getName() + 
		" WHERE Username == '" + user + "' && uuid == '" + uuid + "'";
		List<UserUUIDPair> users = (List<UserUUIDPair>) pm.newQuery(query).execute();
		
		if(users.isEmpty())
			return false;
		else {
			users.get(0).setTime(System.currentTimeMillis());
			return true;
		}
	}
	
	private MessageToClient poll(MessageToServer messageToServer) {
		return null;
		
	}
}
